/*
 * Copyright (c) 2010, Katholieke Universiteit Leuven
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 *     * Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *     * Neither the name of the Katholieke Universiteit Leuven nor the names of
 *       its contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>

#include "net.h"
#include "../api/deployment.h"


static uint8_t ibuffer[LOOCI_COMMANDS_BUFSIZE + sizeof(struct command_request)];
static struct command_request * request = (struct command_request *) ibuffer;
static uint8_t obuffer[LOOCI_COMMANDS_BUFSIZE + sizeof(struct command_reply)];
static struct command_reply * reply = (struct command_reply *) obuffer;

#ifndef LOOCI_CSTORE_PORT
#define LOOCI_CSTORE_PORT "4321"
#endif

#define DEPLOY_BUFSIZE 1024

static int get_deployment_socket(char * address, char * port) {
  int status;
  struct addrinfo hints;
  struct addrinfo * servinfo;
  struct addrinfo * p;
  int sockfd;

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_INET6; // We use IPv6
  hints.ai_socktype = SOCK_STREAM;
  if((status = getaddrinfo(address, port, &hints, &servinfo))!=0) {
    fprintf(stderr, "getaddrinfo error: %s\r\n", gai_strerror(status));
    return -1;
  }

  for(p = servinfo; p != NULL; p = p->ai_next) {
    if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
      continue;
    }
    if(connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
      close(sockfd);
      continue;
    }
    break;
  }

  if(p == NULL) sockfd = -1;
  
  freeaddrinfo(servinfo);
  return sockfd;

}

uint8_t looci_deploy(char * componentFile, char * nodeID) {
  FILE * component = fopen(componentFile, "rb");
  if(component==NULL) {
    fprintf(stderr, "Could not open file to read component\r\n");
    return 0;
  }
  unsigned int filesize = 0;
  struct stat filestat;
  if(fstat(fileno(component), &filestat)==0) {
    filesize = filestat.st_size;
  } else {
    fprintf(stderr, "Could not determine component file size\r\n");
    return 0;
  }
  fprintf(stderr, "Component file size: %u\r\n", filesize);
  if(filesize > 65535) {
    fprintf(stderr, "File too large!\r\n");
    return 0;
  }
  uint16_t length = htons(filesize);
  int socket = get_deployment_socket(nodeID, LOOCI_CSTORE_PORT);
  if(socket == -1) {
    fprintf(stderr, "Could not connect to node!\r\n");
    return 0;
  }
  // Send length
  int sent = send(socket, &length, 2, 0);
  fprintf(stderr, "Sent %u bytes\r\n", sent);
  sleep(1);
  // Send contents
  char buf[DEPLOY_BUFSIZE];
  int read;
  do {
    read = fread(buf, 1, DEPLOY_BUFSIZE, component);
    sent = send(socket, buf, read, 0);
    fprintf(stderr, "Sent %u bytes\r\n", sent);
    sleep(1);
  } while (read==DEPLOY_BUFSIZE);
  fclose(component);
  // receive reply
  uint8_t cid = 0;
  uint8_t first = 0; // Current on-node implementation can return multiple component ids. We return the first one.
  do {
    cid = 0;
    fprintf(stderr, "Reading a component id\r\n");
    recv(socket, &cid, sizeof(uint8_t), 0);
    if(cid!=0) fprintf(stderr,"%u\r\n", cid);
    if(cid!=0 && first==0) first = cid;
  } while (cid!=0);
  close(socket);
  return first;
}

bool looci_remove(uint8_t componentID, char * nodeID) {
  // construct command
  request->cmd = COMMAND_REMOVE;
  request->payload[0] = componentID;
  // send command
  size_t olen = sizeof(uint8_t);
  bool success = send_command(nodeID, request, sizeof(uint8_t), reply, &olen);
  //handle failure of send_command
  if(success == false) {
    return false;
  } else if(olen != sizeof(uint8_t)) { // error in the reply
    return false;
  } else {
    return (bool) reply->payload[0];
  }
  return false;
}

